package com.example.demo2.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo2.mapper.HotelMapper;
import com.example.demo2.model.dtos.QueryDto;
import com.example.demo2.model.pojo.Hotel;
import com.example.demo2.model.pojo.HotelElastic;
import com.example.demo2.model.vos.GroupVo;
import com.example.demo2.model.vos.HotelPageVo;
import com.example.demo2.service.HotelService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Service
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements HotelService {

    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Override
    public HotelPageVo search(QueryDto queryDto) throws IOException {
        //创建一个HotelPageVo
        HotelPageVo hotelPageVo = new HotelPageVo();

        //封装该对象的数据
        SearchRequest searchReq = new SearchRequest("hotel");
        SearchSourceBuilder sourceBuilder = searchReq.source();

        //构建布尔查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //表示多组条件之间是一个and的关系
        List<QueryBuilder> must = boolQuery.must();

        //关键字条件
        String keyword = queryDto.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            must.add(QueryBuilders.matchQuery("all",keyword));
        }
        //城市名称条件
        List<String> cityNames = queryDto.getCityNames();
        if (cityNames!=null && cityNames.size()>0){
            must.add(QueryBuilders.termsQuery("city", cityNames.toArray(new String[0])));
        }
        //星级名称条件
        List<String> starNameNames = queryDto.getStarNames();
        if (starNameNames!=null && starNameNames.size()>0){
            must.add(QueryBuilders.termsQuery("starName", starNameNames.toArray(new String[0])));
        }
        //品牌名称条件
        List<String> brandNames = queryDto.getBrandNames();
        if (brandNames!=null && brandNames.size()>0){
            must.add(QueryBuilders.termsQuery("brand", brandNames.toArray(new String[0])));
        }

        //分页参数
        sourceBuilder.from((queryDto.getPageNum()-1)*queryDto.getSize());
        sourceBuilder.size(queryDto.getSize());

        //聚合查询处理
        sourceBuilder.aggregation(AggregationBuilders.terms("cityAgg").field("city").size(10));
        sourceBuilder.aggregation(AggregationBuilders.terms("starNameAgg").field("starName").size(10));
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(10));

        //判断是否有关键字，如果有，设置关键字高亮

        sourceBuilder.query(boolQuery);

        SearchResponse searchResponse = restHighLevelClient.search(searchReq, RequestOptions.DEFAULT);

        //将查询结果中的数据封装到vo中（封装的是列表数据）
        parseRespnoseList(hotelPageVo,searchResponse);

        //解析分组信息
        parseGroups(hotelPageVo,searchResponse);

        return hotelPageVo;
    }

    /**
     * 解析分组信息
     * @param hotelPageVo
     * @param searchResponse
     */
    private void parseGroups(HotelPageVo hotelPageVo, SearchResponse searchResponse) {
        //获取所有分组
        Aggregations aggregations = searchResponse.getAggregations();

        //获取城市分组
        List<GroupVo> cityGroups = getGroupVos("cityAgg",aggregations,GroupVo.GroupType.CITY);
        hotelPageVo.setCityGroup(cityGroups);

        //获取星级分组
        List<GroupVo> starNameGroups = getGroupVos("starNameAgg",aggregations,GroupVo.GroupType.STAR);
        hotelPageVo.setStarGroup(starNameGroups);

        //获取品牌分组
        List<GroupVo> brandGroups = getGroupVos("brandAgg",aggregations,GroupVo.GroupType.BRAND);
        hotelPageVo.setBrandGroup(brandGroups);
    }

    private List<GroupVo> getGroupVos(String aggName, Aggregations aggregations, GroupVo.GroupType type) {
        ParsedStringTerms cityAgg = aggregations.get(aggName);
        List<? extends Terms.Bucket> buckets = cityAgg.getBuckets();
        List<GroupVo> groupVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String name = bucket.getKeyAsString();
            long count = bucket.getDocCount();
            GroupVo groupVo = new GroupVo();
            groupVo.setType(type);
            groupVo.setName(name);
            groupVo.setCount((int) count);
            groupVos.add(groupVo);
        }
        return groupVos;
    }

    /**
     * 解析列表数据并封装
     * @param hotelPageVo
     * @param searchResponse
     */
    private void parseRespnoseList(HotelPageVo hotelPageVo, SearchResponse searchResponse) {
        SearchHits hits = searchResponse.getHits();
        //获取总记录数
        long count = hits.getTotalHits().value;
        hotelPageVo.setTotal(count);

        //封装列表数据
        SearchHit[] searchHits = hits.getHits();
        List<HotelElastic> hotelElastics = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            String json = searchHit.getSourceAsString();
            HotelElastic hotelElastic = JSON.parseObject(json, HotelElastic.class);

            hotelElastics.add(hotelElastic);
        }

        hotelPageVo.setList(hotelElastics);
    }
}
